package battlecruiser.ship.placement.ship;

import battlecruiser.board.ObservableBoard;
import battlecruiser.board.cell.coordinate.Coordinate;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @author tymoshya
 * @since 14.06.12
 */
public class Ship {

    private static Logger LOG = Logger.getLogger(Ship.class);

    public static Ship random(ObservableBoard observableBoard) {
        return random(observableBoard, randomShipType());
    }

    public static Ship random(ObservableBoard observableBoard, ShipType shipType) {
        Ship result;
        while (true) {
            result = new Ship(shipType, randomDirection(), randomCoordinate(observableBoard.getFreeCoordinates()));
            if (observableBoard.isPlaceable(result)) {
                break;
            } else {
                LOG.warn("Can not place the ship: " + result);
            }
        }
        return result;
    }

    private static Coordinate randomCoordinate(List<Coordinate> coordinates) {
        Random random = new Random();
        return coordinates.get(random.nextInt(coordinates.size()));
    }

    private static ShipDirection randomDirection() {
        ShipDirection result;
        Random random = new Random();
        if (random.nextBoolean()) {
            result = ShipDirection.VERTICAL;
        } else {
            result = ShipDirection.HORIZONTAL;
        }
        return result;
    }

    private static ShipType randomShipType() {
        Random random = new Random();
        return ShipType.fromInteger(random.nextInt(4) + 1);
    }

    private ShipType type;
    private ShipDirection direction;
    private Coordinate firstDeckCoordinate;

    public Ship(ShipType type, ShipDirection direction, Coordinate firstDeckCoordinate) {
        this.type = type;
        this.direction = direction;
        this.firstDeckCoordinate = firstDeckCoordinate;
    }

    public void rotate() {
        ShipDirection oldDirection = getDirection();
        ShipDirection newDirection = oldDirection == ShipDirection.VERTICAL ?
                ShipDirection.HORIZONTAL : ShipDirection.VERTICAL;
        setDirection(newDirection);
    }

    public void setDirection(ShipDirection direction) {
        this.direction = direction;
    }

    public ShipType getType() {
        return type;
    }

    public ShipDirection getDirection() {
        return direction;
    }

    public Coordinate getFirstDeckCoordinate() {
        return firstDeckCoordinate;
    }

    public Coordinate getLastDeckCoordinate() {
        final int firstI = firstDeckCoordinate.getI();
        final int firstJ = firstDeckCoordinate.getJ();
        int lastI;
        int lastJ;
        final int step = type.getNumberOfDecks() - 1;

        if (direction == ShipDirection.HORIZONTAL) {
            lastI = firstI;
            lastJ = firstJ + step;
        } else {
            lastI = firstI + step;
            lastJ = firstJ;
        }

        return new Coordinate(lastI, lastJ);
    }

    public List<Coordinate> getCoordinates() {
        List<Coordinate> shipCoordinates = new ArrayList<Coordinate>();

        Coordinate lastDeckCoordinate = getLastDeckCoordinate();
        if (getDirection() == ShipDirection.HORIZONTAL) {
            int i = firstDeckCoordinate.getI();
            for (int j = firstDeckCoordinate.getJ(); j <= lastDeckCoordinate.getJ(); j++) {
                shipCoordinates.add(new Coordinate(i, j));
            }
        } else {
            int j = firstDeckCoordinate.getJ();
            for (int i = firstDeckCoordinate.getI(); i <= lastDeckCoordinate.getI(); i++) {
                shipCoordinates.add(new Coordinate(i, j));
            }
        }

        return shipCoordinates;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Ship)) return false;

        Ship ship = (Ship) o;

        if (direction != ship.direction) return false;
        if (firstDeckCoordinate != null ? !firstDeckCoordinate.equals(ship.firstDeckCoordinate) : ship.firstDeckCoordinate != null)
            return false;
        if (type != ship.type) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = type != null ? type.hashCode() : 0;
        result = 31 * result + (direction != null ? direction.hashCode() : 0);
        result = 31 * result + (firstDeckCoordinate != null ? firstDeckCoordinate.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return type + " " + direction + " " + firstDeckCoordinate;
    }
}
